"""
File operation tools for FastMCP
"""

from fastmcp import FastMCP, Context
from typing import Optional, List
import os
import json
from pathlib import Path
import aiofiles


class FileTools:
    """File operation tools for FastMCP."""

    def __init__(self, mcp: FastMCP):
        self.mcp = mcp

    def register(self):
        """Register all file tools with FastMCP."""
        self.mcp.tool(
            name="list_directory",
            description="List files and directories in a given path",
            tags={"file", "directory", "public"},
        )(self.list_directory)

        self.mcp.tool(
            name="read_text_file",
            description="Read the contents of a text file",
            tags={"file", "read", "text", "public"},
        )(self.read_text_file)

        self.mcp.tool(
            name="write_text_file",
            description="Write text content to a file",
            tags={"file", "write", "text", "public"},
        )(self.write_text_file)

        self.mcp.tool(
            name="file_info",
            description="Get detailed information about a file or directory",
            tags={"file", "info", "metadata", "public"},
        )(self.file_info)

        self.mcp.tool(
            name="search_files",
            description="Search for files by name pattern in a directory",
            tags={"file", "search", "pattern", "public"},
        )(self.search_files)

    @staticmethod
    def list_directory(path: str = ".", show_hidden: bool = False) -> dict:
        """List the contents of a directory."""
        try:
            dir_path = Path(path)
            if not dir_path.exists():
                return {"error": f"Path does not exist: {path}"}

            if not dir_path.is_dir():
                return {"error": f"Path is not a directory: {path}"}

            items = []
            for item in dir_path.iterdir():
                # Skip hidden files unless requested
                if not show_hidden and item.name.startswith("."):
                    continue

                item_info = {
                    "name": item.name,
                    "type": "directory" if item.is_dir() else "file",
                    "size": item.stat().st_size if item.is_file() else None,
                }
                items.append(item_info)

            # Sort by type (directories first) then by name
            items.sort(key=lambda x: (x["type"] != "directory", x["name"].lower()))

            return {
                "path": str(dir_path.absolute()),
                "item_count": len(items),
                "items": items,
            }

        except PermissionError:
            return {"error": f"Permission denied: {path}"}
        except Exception as e:
            return {"error": f"Error listing directory: {str(e)}"}

    @staticmethod
    async def read_text_file(
        file_path: str, ctx: Context, max_size: int = 10000
    ) -> dict:
        """Read a text file. Limited to max_size characters for safety."""
        try:
            path = Path(file_path)
            if not path.exists():
                return {"error": f"File does not exist: {file_path}"}

            if not path.is_file():
                return {"error": f"Path is not a file: {file_path}"}

            # Check file size
            file_size = path.stat().st_size
            if file_size > max_size:
                await ctx.warning(
                    f"File is large ({file_size} bytes), reading first {max_size} characters"
                )

            async with aiofiles.open(path, "r", encoding="utf-8") as f:
                if file_size > max_size:
                    content = await f.read(max_size)
                    truncated = True
                else:
                    content = await f.read()
                    truncated = False

            return {
                "file_path": str(path.absolute()),
                "content": content,
                "file_size": file_size,
                "truncated": truncated,
                "characters_read": len(content),
            }

        except UnicodeDecodeError:
            return {
                "error": f"File is not a valid text file (encoding error): {file_path}"
            }
        except PermissionError:
            return {"error": f"Permission denied: {file_path}"}
        except Exception as e:
            return {"error": f"Error reading file: {str(e)}"}

    @staticmethod
    async def write_text_file(
        file_path: str, content: str, ctx: Context, overwrite: bool = False
    ) -> dict:
        """Write text content to a file."""
        try:
            path = Path(file_path)

            # Check if file exists and overwrite flag
            if path.exists() and not overwrite:
                return {
                    "error": f"File already exists and overwrite=False: {file_path}"
                }

            # Create parent directories if they don't exist
            path.parent.mkdir(parents=True, exist_ok=True)

            await ctx.info(f"Writing {len(content)} characters to {file_path}")

            async with aiofiles.open(path, "w", encoding="utf-8") as f:
                await f.write(content)

            file_size = path.stat().st_size

            return {
                "file_path": str(path.absolute()),
                "characters_written": len(content),
                "file_size": file_size,
                "created": not path.existed() if hasattr(path, "existed") else True,
            }

        except PermissionError:
            return {"error": f"Permission denied: {file_path}"}
        except Exception as e:
            return {"error": f"Error writing file: {str(e)}"}

    @staticmethod
    def file_info(path: str) -> dict:
        """Get detailed information about a file or directory."""
        try:
            file_path = Path(path)
            if not file_path.exists():
                return {"error": f"Path does not exist: {path}"}

            stat = file_path.stat()

            info = {
                "path": str(file_path.absolute()),
                "name": file_path.name,
                "type": "directory" if file_path.is_dir() else "file",
                "size": stat.st_size,
                "modified_time": stat.st_mtime,
                "created_time": stat.st_ctime,
                "permissions": oct(stat.st_mode)[-3:],
                "is_readable": os.access(file_path, os.R_OK),
                "is_writable": os.access(file_path, os.W_OK),
                "is_executable": os.access(file_path, os.X_OK),
            }

            if file_path.is_file():
                info["extension"] = file_path.suffix
                info["stem"] = file_path.stem

            return info

        except PermissionError:
            return {"error": f"Permission denied: {path}"}
        except Exception as e:
            return {"error": f"Error getting file info: {str(e)}"}

    @staticmethod
    def search_files(
        directory: str = ".",
        pattern: str = "*",
        recursive: bool = False,
        max_results: int = 100,
    ) -> dict:
        """Search for files matching a pattern in a directory."""
        try:
            dir_path = Path(directory)
            if not dir_path.exists():
                return {"error": f"Directory does not exist: {directory}"}

            if not dir_path.is_dir():
                return {"error": f"Path is not a directory: {directory}"}

            # Use glob to find matching files
            if recursive:
                matches = list(dir_path.rglob(pattern))
            else:
                matches = list(dir_path.glob(pattern))

            # Limit results
            if len(matches) > max_results:
                matches = matches[:max_results]
                truncated = True
            else:
                truncated = False

            # Format results
            results = []
            for match in matches:
                results.append(
                    {
                        "path": str(match.absolute()),
                        "name": match.name,
                        "type": "directory" if match.is_dir() else "file",
                        "size": match.stat().st_size if match.is_file() else None,
                    }
                )

            return {
                "directory": str(dir_path.absolute()),
                "pattern": pattern,
                "recursive": recursive,
                "matches_found": len(matches),
                "results": results,
                "truncated": truncated,
                "max_results": max_results,
            }

        except PermissionError:
            return {"error": f"Permission denied: {directory}"}
        except Exception as e:
            return {"error": f"Error searching files: {str(e)}"}

    def register(self):
        """Register all file tools with FastMCP."""
        self.mcp.tool(
            name="list_directory",
            description="List files and directories in a given path",
            tags={"file", "directory", "public"},
        )(self.list_directory)

        self.mcp.tool(
            name="read_text_file",
            description="Read the contents of a text file",
            tags={"file", "read", "text", "public"},
        )(self.read_text_file)

        self.mcp.tool(
            name="write_text_file",
            description="Write text content to a file",
            tags={"file", "write", "text", "public"},
        )(self.write_text_file)

        self.mcp.tool(
            name="file_info",
            description="Get detailed information about a file or directory",
            tags={"file", "info", "metadata", "public"},
        )(self.file_info)

        self.mcp.tool(
            name="search_files",
            description="Search for files by name pattern in a directory",
            tags={"file", "search", "pattern", "public"},
        )(self.search_files)
